/*=+--+=#=+--     Unmanned Aerial System Management Software      --+=#=+--+=#*\
|          Copyright (C) 2011 Regents of the University of Colorado.           |
|                             All Rights Reserved.                             |

     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License version 2 as
     published by the Free Software Foundation.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

            Jack Elston                       Cory Dixon                        
|           elstonj@colorado.edu              dixonc@colorado.edu              |
|                                                                              |
\*=+--+=#=+--                 --+=#=+--+=#=+--                    --+=#=+--+=#*/

/*!
  \file networkAppliance.cxx
  \brief 

  \author Jack Elston
  $Date: 2005/02/12 00:20:45 $
*/

#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <socket.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/ether.h>

#include "networkAppliance.h"
#include "color.h"

/*<-------Defines--------->*/
/*<------End Defines------>*/

/*<---Global Variables---->*/
/*! \brief determines level of output to user. */
extern bool verbose;
extern String vehicleID;
extern String logDir;
/*<-End Global Variables-->*/

NetworkAppliance::NetworkAppliance(String a_name) 
  : name(a_name), dataDist("Distribution Node",logDir)
{
	//dataDist = new Data_Distribution("Distribution Node",logDir);

	/*<-Initialize Shared Mem->*/
	pthread_mutex_init(&shm.mtx, NULL);

	num_interfaces = 0;
	for(int i=0; i<MAX_INTERFACES; i++)
		interfaces[i] = NULL;

	/*<---Data Distr Thread--->*/

	bool status = dataDist.init(&shm);
	if ( status == false) {
		cerr << endl << "\tERROR - " << dataDist.getName() << " init" << endl;
		dataDist.shutdown();
	} else if ( dataDist.start(500, 0) != OK )
		cerr << endl << "\tERROR - " << dataDist.getName() << " start" << endl;
	usleep(1000);
}

NetworkAppliance::~NetworkAppliance()
{
	pthread_mutex_destroy(&shm.mtx);
}

/************************************************************\
|                NetworkAppliance::addFifo                   |
\************************************************************/
/*!
  \brief
  \param
  \pre
  \post
  \return
*/
void NetworkAppliance::link(Interface * iface)
{
	interfaces[num_interfaces++]=iface;
	dataDist.registerInterface(iface);
}

void NetworkAppliance::start()
{
	if( !shm.clients.add(vehicleID) ) {
		cout << "NetworkAppliance::start -- could not add myself to client list" << endl;
		return;
	}

	/* register local capabilites */
	Client * me   = shm.clients.getClient(0);
	int i =0;
	SystemCharacter_t * system;
	while((system = shm.capabilities.get(i)) != NULL) {
		me->capabilities.add(system);
		i++;
	}
	// FIXME -- is this necessary?  are there returns on TCP sockets and a broadcast message?
	/* register local capabilites */

	setMyIP();

	for(int i = 0; i < num_interfaces; i++)
		if( interfaces[i] )	
			interfaces[i]->start(&shm);
}

void NetworkAppliance::stop()
{
	for(int i = 0; i < num_interfaces; i++)
		if( interfaces[i] )	
			interfaces[i]->stop();
}

void NetworkAppliance::setMyIP()
{
	char          buf[1024];
	struct ifconf ifc;
	struct ifreq *ifr;
	int           sck;
	int           nInterfaces;
	int           i;

	Client * me = shm.clients.getClient(0);
	if( !me ){
		cout << "NetworkAppliance::start -- could not get myself" << endl;
		return;
	}

	cout << endl << "Detecting IPV4 addresses of this host: ........." << endl;

	// Get a socket handle. 
	if( (sck = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) {
		perror("socket");
		return;
	}

	// Query available interfaces. 
	ifc.ifc_len = sizeof(buf);
	ifc.ifc_buf = buf;
	if(ioctl(sck, SIOCGIFCONF, &ifc) < 0) {
		perror("ioctl(SIOCGIFCONF)");
		return;
	}

	// Loop through each network interface
	ifr         = ifc.ifc_req;
	nInterfaces = ifc.ifc_len / sizeof(struct ifreq);

	for(i = 0; i < nInterfaces; i++)
	{
		struct ifreq *item = &ifr[i];

		// duplicate, skip it
		if (ifr[i].ifr_addr.sa_data == ifr[i+1].ifr_addr.sa_data) 
			continue; 

		// get flags
		if(ioctl(sck, SIOCGIFFLAGS, item)) {
			perror("ioctl(SIOCGIFFLAGS)");
			continue; 
		}

		// ignore the loopback interface
		if( strcmp("lo", item->ifr_name) == 0) 
			continue;

		struct in_addr if_addr;
		struct in_addr bc_addr;

#define get_sin_addr(item) ((struct sockaddr_in *)&item->ifr_addr)->sin_addr
		if_addr.s_addr = get_sin_addr(item).s_addr;

		// provide the user (if there is one) some output
		printf("%s", GREEN);
		printf("\t%s", item->ifr_name);
		printf("%s", CYAN);
		printf("\t%s", inet_ntoa(get_sin_addr(item)) );

/*
		// get the mac
		if (0 == ioctl(sck, SIOCGIFHWADDR, item)) {
			unsigned char * u = (unsigned char *) &item->ifr_addr.sa_data;
			printf("\t%2.2X.%2.2X.%2.2X.%2.2X.%2.2X.%2.2X",
			        u[0], u[1], u[2], u[3], u[4], u[5]);
		}

		// get the netmask
		if (0 == ioctl(sck, SIOCGIFNETMASK, item) &&
				strcmp("255.255.255.255", inet_ntoa(get_sin_addr(item)))) {
			printf("\t%s", inet_ntoa(get_sin_addr(item)));
		}
*/

		// get the broadcast address
		if (item->ifr_flags & IFF_BROADCAST) {
			if (0 == ioctl(sck, SIOCGIFBRDADDR, item) && strcmp("0.0.0.0", inet_ntoa(get_sin_addr(item)))) {
				bc_addr.s_addr = get_sin_addr(item).s_addr;
				printf("%s", CYAN);
				printf("\t[%s]", inet_ntoa(bc_addr));
			}
		}
		printf("%s\n", ATTROFF);

		
		//-----------------------------------------------
		// set address of local client
		//-----------------------------------------------
		//if((if_addr.s_addr & inet_addr("255.0.0.0")) == inet_addr("6.0.0.0")) {  //FIXME!! this was put in to deal with B.A.T.M.A.N interface timeout issues
			shm.clients.addAddr(me, if_addr, bc_addr);
		//}

	}
	printf("\n");

	return;
}
